﻿using GLDotNet;
using GLDotNet.Toolkit;
using System;

namespace GLDotNetGraphics2DDemo
{
    public class Graphics2DDemo : GLApplication
    {
        GraphicsDevice graphicsDevice;
        Texture2D texture;
        SpriteBatch spriteBatch;
        BasicSpriteBatchShaderProgram shader;

        Vector2 delta;
        float rotation;
        Vector2 position;
        float animationTime;
        int animationFrame;

        public Graphics2DDemo()
            : base(new GLContextParams() { VersionMajor = 3, VersionMinor = 3, ForwardCompatible = true })
        {
            this.Window.Title = "Graphics2D Demo - " + (Environment.Is64BitProcess ? "x64" : "x86");
            this.Window.Resize += this.Window_Resize;
            this.Window.KeyDown += this.Window_KeyDown;
            this.Window.KeyUp += this.Window_KeyUp;
        }
                
        protected override void Initialize()
        {
            this.graphicsDevice = new GraphicsDevice(this.GLContext);

            this.graphicsDevice.ClearColor(Color4.CornflowerBlue);

            this.graphicsDevice.Enable(EnableCap.Blend);
            this.graphicsDevice.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
            
            this.graphicsDevice.Viewport = Viewport.FromSize(this.Window.Size);

            this.texture = Texture2D.FromFile(this.graphicsDevice, "Planes.png", new Texture2DParams()
            {
                ColorKey = GLUtility.MakePixelRGBA(255, 0, 255, 255)
            });

            this.spriteBatch = new SpriteBatch(this.graphicsDevice)
            {
                SceneSize = new Size(this.Window.Width, this.Window.Height)
            };

            this.shader = new BasicSpriteBatchShaderProgram(this.graphicsDevice);

            this.rotation = 0;
            this.position = new Vector2(400, 300);
        }

        protected override void Shutdown()
        {
            this.texture.Dispose();
            this.spriteBatch.Dispose();
            this.shader.Dispose();
        }

        private void Window_Resize(object sender, EventArgs e)
        {
            this.graphicsDevice.Viewport = Viewport.FromSize(this.Window.Size);
            this.spriteBatch.SceneSize = this.Window.Size;
        }

        private void Window_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.Key)
            {
                case Key.Left: this.delta.X -= 1.0f; break;
                case Key.Right: this.delta.X += 1.0f; break;
                case Key.Up: this.delta.Y -= 1.0f; break;
                case Key.Down: this.delta.Y += 1.0f; break;
            }
        }

        private void Window_KeyUp(object sender, KeyEventArgs e)
        {
            switch (e.Key)
            {
                case Key.Left: this.delta.X += 1.0f; break;
                case Key.Right: this.delta.X -= 1.0f; break;
                case Key.Up: this.delta.Y += 1.0f; break;
                case Key.Down: this.delta.Y -= 1.0f; break;
            }
        }

        private static void RotateAboutOrigin(ref Vector2 point, ref Vector2 origin, float rotation)
        {
            Vector2 u = point - origin; // point relative to origin  

            if (u == Vector2.Zero)
                return;

            float a = (float)Math.Atan2(u.Y, u.X); // angle relative to origin  
            a += rotation; // rotate  

            // u is now the new point relative to origin
            float length = u.Length();
            u = new Vector2((float)Math.Cos(a) * length, (float)Math.Sin(a) * length);

            point = u + origin;
        }

        protected override void Update(TimeSpan elapsed)
        {
            this.rotation += this.delta.X * (float)elapsed.TotalSeconds * 100.0f;

            Vector2 newPosition = new Vector2(this.position.X, this.position.Y - ((float)elapsed.TotalSeconds * 100.0f));
            RotateAboutOrigin(ref newPosition, ref this.position, MathHelper.ToRadians(this.rotation));

            this.position = newPosition;

            if (this.position.X < 0)
            {
                this.position.X += this.Window.Width;
            }
            else if (this.position.X >= this.Window.Width)
            {
                this.position.X -= this.Window.Width;
            }

            if (this.position.Y < 0)
            {
                this.position.Y += this.Window.Height;
            }
            else if (this.position.Y >= this.Window.Height)
            {
                this.position.Y -= this.Window.Height;
            }

            this.animationTime += (float)elapsed.TotalSeconds;
            if (this.animationTime > 0.025f)
            {
                this.animationTime -= 0.025f;
                this.animationFrame++;
                
                if (this.animationFrame >= 3)
                {
                    this.animationFrame -= 3;
                }
            }
        }

        protected override void Draw()
        {
            this.graphicsDevice.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
                        
            this.spriteBatch.Begin(this.shader);

            Rectangle source = new Rectangle(2 + (this.animationFrame * (64 + 2)), 2, 64, 64);
            this.spriteBatch.Draw(this.texture, Color4.White, this.position, source, new Vector2(32, 32), Vector2.One, MathHelper.ToRadians(this.rotation));

            this.spriteBatch.End();

            this.graphicsDevice.SwapBuffers();
        }

        [STAThread]
        public static void Main()
        {
            using (Graphics2DDemo demo = new Graphics2DDemo())
                demo.Run();
        }
    }
}
